package com.link.base.base.common.service;

import com.link.base.base.monitor.MonitorObjectChange;
import com.link.base.base.user.model.User;
import com.link.base.base.common.dao.mybatis.mapper.MenuMapper;
import com.link.base.base.common.model.Menu;
import com.link.base.base.common.model.MenuAndCorpModel;
import com.link.base.base.common.model.MenuTree;
import com.link.base.base.common.model.NewMenu;
import com.link.base.base.securitypage.model.SecurityPage;
import com.link.base.base.securitypage.model.SecurityPageUrl;
import com.link.base.base.securitypage.service.SecurityPageService;
import com.link.base.base.securitypage.service.SecurityPageUrlService;
import com.link.base.user.model.CoreUser;
import com.link.core.cllog.LCLogger;
import com.link.core.basic.service.ServiceException;
import com.link.core.util.AppConstants;
import com.link.core.util.UserUtil;
import com.link.core.basic.dao.mapper.BasicMapper;
import com.link.core.basic.database.DataSource;
import com.link.core.basic.database.DataSourceType;
import com.link.core.basic.service.BasicServiceImpl;
import com.link.core.util.RedisUtil;
import com.link.core.util.StringUtils;

import javax.annotation.Resource;

import com.link.core.util.redisclient.LinkRedisClient;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;


import javax.servlet.http.HttpSession;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Copyright  hande Company. All Rights Reserved.
 * 菜单服务模块
 * @author unknwon
 * @version 1.0
 * @date: 2018/11/9 9:27
 */

@Service
public class MenuServiceImpl extends BasicServiceImpl<Menu> implements MenuService {
    @Resource
    private MenuMapper menuMapper;
    @Resource
    private SecurityPageService securityPageService;
    @Resource
    private SecurityPageUrlService securityPageUrlService;
    @Resource
    private MonitorObjectChange monitorObjectChange;

    @Override
    public BasicMapper<Menu> getBasicMapper() {
        return menuMapper;
    }

    /**
     * 根据菜单类型获取菜单列表
     *
     * @param menuType
     * @param userId
     * @param session
     * @return
     * @throws ServiceException
     */
    @Override
    public List<Menu> getMenuListByMenuTyepSelect(String menuType, Long userId, HttpSession session)
            throws ServiceException {
        List<Menu> list = null;
        CoreUser user = UserUtil.getUser();
        if (userId != null) {
            Menu param = new Menu();
            Menu subParam = new Menu();
            param.setMenuType(menuType);
            param.setUserId(userId);
            String langName = user.getLoginLanguage();
            param.setLoginLanguage(langName);
            subParam.setUserId(userId);
            // 安全性菜单查询参数
            subParam.setMenuGrade("2");
            subParam.setCorpid(user.getCorpid());
            subParam.setMenuType(menuType);
            subParam.setLoginLanguage(langName);
            String ctxPath = "";
            try {
                list = menuMapper.queryMenuListByTypeSelect(param);
                List<Menu> subList = querySubItemListByDutyItemIdSelect(subParam);
                list = menuAuth(list, subList, ctxPath);
            } catch (Exception e) {
                LCLogger.withException(e);
                throw new ServiceException(e.getMessage());
            }
        }

        return removeMenuWithoutSecurity(list);
    }

    /**
     * 处理菜单安全性
     * @author heqiqian
     * @param secMenuList 二级菜单
     * @param trdMenuList 安全性菜单
     * @param ctxPath     工程路径
     * @return 处理好的二级菜单，里面包含安全性菜单
     * @date 2018年7月20日
     */
    private List<Menu> menuAuth(List<Menu> secMenuList, List<Menu> trdMenuList, String ctxPath) {
        if (ctxPath == null) {
            ctxPath = "";
        }
        if (secMenuList != null) {
            // 二级菜单权限计算
            secMenuList = menuAuthProcessor(secMenuList, null);
            // 安全性菜单权限计算
            trdMenuList = menuAuthProcessor(trdMenuList, secMenuList);
            // 需要保证菜单顺序
            LinkedHashMap<Long, Menu> map = new LinkedHashMap<Long, Menu>(secMenuList.size());
            // 二级菜单合并，权限最终确定
            for (int i = 0; i < secMenuList.size(); i++) {
                Menu menu = secMenuList.get(i);
                String u = menu.getMenuUrl();
                Menu mapMenu = map.get(menu.getId());
                String url = null;
                if (menu.getMenuUrl() != null) {
                    if (u.trim().toLowerCase().startsWith("http://")
                            || u.trim().toLowerCase().startsWith("https://")) {
                        url = paramReplace(u.trim());
                    } else {
                        url = ctxPath + menu.getMenuUrl();
                    }
                    menu.setMenuUrl(url);
                }
                if (mapMenu == null) {
                    map.put(menu.getId(), menu);
                } else {
                    menuAuthMerge(menu, mapMenu);
                }
            }
            // 安全性菜单合并，权限最终确定，并设置到二级菜单下
            for (int i = 0; i < trdMenuList.size(); i++) {
                Menu subMenu = trdMenuList.get(i);
                Menu menu = map.get(subMenu.getParentMenuId());
                if (subMenu.getMenuUrl() != null) {
                    String url = null;
                    if (subMenu.getMenuUrl().trim().toLowerCase().startsWith("http://")
                            || subMenu.getMenuUrl().trim().toLowerCase().startsWith("https://")) {
                        url = paramReplace(subMenu.getMenuUrl().trim());
                    } else {
                        url = ctxPath + subMenu.getMenuUrl();
                    }
                    subMenu.setMenuUrl(url);
                }
                if (menu == null) {
                    continue;
                }
                if (menu.getSubMenus() != null) {
                    menuAuthMerge(subMenu, menu.getSubMenus());
                } else {
                    List<Menu> subMenus = new ArrayList<Menu>();
                    subMenus.add(subMenu);
                    menu.setSubMenus(subMenus);
                }
            }
            secMenuList = new ArrayList<Menu>(map.values());
        }
        return secMenuList;
    }

    /**
     * 排除没有安全性菜单的二级菜单对象并返回二级菜单对象List
     *
     * @param list
     * @return
     */
    private List<Menu> removeMenuWithoutSecurity(List<Menu> list) {
        if (list == null) {
            return null;
        }
        for (int i = 0; i < list.size(); i++) {
            Menu menu = list.get(i);
            boolean flag = (menu.getSubMenus() == null || (menu.getSubMenus() != null && menu.getSubMenus().isEmpty()));
            if (flag) {
                menu.setMenuUrl("/portal/401.jsp");
            }
        }
        return checkHomePageMenu(list);
    }

    /**
     * 检查是否为首页类型的子菜单, 是则只返回第一个
     *
     * @param list
     * @return
     */
    private List<Menu> checkHomePageMenu(List<Menu> list) {
        if (list == null) {
            return null;
        }
        String flag = "Y";
        if (list.size() > 1) {
            if (flag.equals(list.get(0).getParentHomePageFlag())) {
                //现在需要一次获取不同一级菜单下的二级菜单，所以有的菜单是首页类型有的不是，循环一次只保留所有首页类型一级菜单的第一个子菜单
                for (int i = 1; i < list.size(); i++) {
                    if ("Y".equals(list.get(i).getParentHomePageFlag())) {
                        if (list.get(i).getParentMenuId().equals(list.get(i - 1).getParentMenuId())) {
                            list.remove(i);
                            i--;
                        } else {
                            Menu menu = list.get(0);
                            if (menu.getSubMenus().size() > 1) {
                                Menu secMenu = menu.getSubMenus().get(0);
                                menu.getSubMenus().clear();
                                menu.getSubMenus().add(secMenu);
                            }
                        }
                    }
                }
            }
        }
        return list;
    }

    private String paramReplace(String url) {
        CoreUser user = UserUtil.getUser();
        String username = user.getUsername();
        Long currentLoginId = user.getId();
        Long currentLoginPositionId = user.getPostnId();
        String trueUrl = url;
        if (username != null) {
            trueUrl = trueUrl.replaceAll("&username&", username);
        } else {
            trueUrl = trueUrl.replaceAll("&username&", "");
        }
        if (currentLoginId != null) {
            trueUrl = trueUrl.replaceAll("&currentLoginId&", String.valueOf(currentLoginId));
        } else {
            trueUrl = trueUrl.replaceAll("&currentLoginId&", "");
        }
        if (currentLoginPositionId != null) {
            trueUrl = trueUrl.replaceAll("&currentLoginPositionId&", String.valueOf(currentLoginPositionId));
        } else {
            trueUrl = trueUrl.replaceAll("&currentLoginPositionId&", "");
        }
        Long currentLoginOrgId = user.getOrgId();
        if (currentLoginOrgId != null) {
            trueUrl = trueUrl.replaceAll("&currentLoginOrgId&", String.valueOf(currentLoginOrgId));
        } else {
            trueUrl = trueUrl.replaceAll("&currentLoginOrgId&", "");
        }
        Long corpId = user.getCorpid();
        if (corpId != null) {
            trueUrl = trueUrl.replaceAll("&corpid&", String.valueOf(corpId));
        } else {
            trueUrl = trueUrl.replaceAll("&corpid&", "");
        }
        String fstname = user.getFirstName();
        if (fstname != null) {
            trueUrl = trueUrl.replaceAll("&firstname&", fstname);
        } else {
            trueUrl = trueUrl.replaceAll("&firstname&", "");
        }
        String postname = user.getPostnName();
        if (postname != null) {
            trueUrl = trueUrl.replaceAll("&positionname&", postname);
        } else {
            trueUrl = trueUrl.replaceAll("&positionname&", "");
        }
        String orgName = user.getOrgName();
        if (orgName != null) {
            trueUrl = trueUrl.replaceAll("&orgname&", orgName);
        } else {
            trueUrl = trueUrl.replaceAll("&orgname&", "");
        }
        String accessToken = user.getUserToken();
        if (accessToken != null) {
            trueUrl = trueUrl.replaceAll("&access_token&", accessToken);
        } else {
            trueUrl = trueUrl.replaceAll("&access_token&", "");
        }
        return trueUrl;
    }

    /**
     * <p>
     * 菜单列表的只读、可添加、可修改等权限的处理计算,返回最终结果
     * </p>
     *
     * @param list
     * @param parentList
     * @return
     * @author yrf
     */
    @Override
    public List<Menu> menuAuthProcessor(List<Menu> list, List<Menu> parentList) {
        if (list == null) {
            return null;
        }
        for (int i = 0; i < list.size(); i++) {
            Menu menu = list.get(i);
            // 父菜单
            Menu parentMenu = null;
            /**
             * 处理可导出标记，与只读标记无关的标记
             */
            if ("N".equals(menu.getDutyExportFlag())) {
                // 职责下不可导出
                menu.setExportFlag("N");
            } else {
                if ("Y".equals(menu.getExportFlag())) {
                    // 职责下及菜单原本都可导出，则查找父菜单
                    if (parentList != null) {
                        for (int j = 0; j < parentList.size(); j++) {
                            parentMenu = parentList.get(j);
                            if (parentMenu.getId().equals(menu.getParentMenuId())
                                    // 父子菜单id、职责id相等才视为父子菜单
                                    && parentMenu.getDutyId().equals(menu.getDutyId())) {
                                // 取父菜单标记作为最终标记
                                menu.setExportFlag(parentMenu.getExportFlag());
                                break;
                            } else {
                                parentMenu = null;
                            }
                        }
                    }
                }
            }
            /**
             * 处理与数据修改相关权限标记
             */
            // 职责下菜单只读
            if ("Y".equals(menu.getDutyReadonlyFlag())) {
                menu.setReadonlyFlag("Y");
                menu.setAddFlag("N");
                menu.setEditFlag("N");
                menu.setDeleteFlag("N");
            } else {
                // 菜单原本只读
                if ("Y".equals(menu.getReadonlyFlag())) {
                    menu.setReadonlyFlag("Y");
                    menu.setAddFlag("N");
                    menu.setEditFlag("N");
                    menu.setDeleteFlag("N");
                } else {
                    String parentReadonlyFlag = "N";
                    String parentAddFlag = "Y";
                    String parentEditFlag = "Y";
                    String parentDeleteFlag = "Y";
                    String parentImportFlag = "Y";
                    if (parentList != null) {
                        if (parentMenu == null) {
                            for (int j = 0; j < parentList.size(); j++) {
                                parentMenu = parentList.get(j);
                                if (parentMenu.getId().equals(menu.getParentMenuId())
                                        // 父子菜单id、职责id相等才视为父子菜单
                                        && parentMenu.getDutyId().equals(menu.getDutyId())) {
                                    parentReadonlyFlag = parentMenu.getReadonlyFlag();
                                    parentAddFlag = parentMenu.getAddFlag();
                                    parentEditFlag = parentMenu.getEditFlag();
                                    parentDeleteFlag = parentMenu.getDeleteFlag();
                                    parentImportFlag = parentMenu.getImportFlag();
                                }
                            }
                        } else {
                            // 前面计算可导出标记时已获取到父菜单对象
                            parentReadonlyFlag = parentMenu.getReadonlyFlag();
                            parentAddFlag = parentMenu.getAddFlag();
                            parentEditFlag = parentMenu.getEditFlag();
                            parentDeleteFlag = parentMenu.getDeleteFlag();
                            parentImportFlag = parentMenu.getImportFlag();
                        }
                    }
                    // 父菜单只读
                    if ("Y".equals(parentReadonlyFlag)) {
                        menu.setReadonlyFlag("Y");
                        menu.setAddFlag("N");
                        menu.setEditFlag("N");
                        menu.setDeleteFlag("N");
                        menu.setImportFlag("N");
                    } else {
                        menu.setReadonlyFlag("N");
                        // 菜单及父菜单可添加的情况下再判断当前职责下菜单是否可添加
                        if ("Y".equals(menu.getAddFlag()) && "Y".equals(parentAddFlag)) {
                            menu.setAddFlag(menu.getDutyAddFlag());
                        } else {
                            menu.setAddFlag("N");
                        }
                        // 菜单及父菜单可编辑的情况下再判断当前职责下菜单是否可编辑
                        if ("Y".equals(menu.getEditFlag()) && "Y".equals(parentEditFlag)) {
                            menu.setEditFlag(menu.getDutyEditFlag());
                        } else {
                            menu.setEditFlag("N");
                        }
                        // 菜单及父菜单可删除的情况下再判断当前职责下菜单是否可删除
                        if ("Y".equals(menu.getDeleteFlag()) && "Y".equals(parentDeleteFlag)) {
                            menu.setDeleteFlag(menu.getDutyDeleteFlag());
                        } else {
                            menu.setDeleteFlag("N");
                        }
                        // 菜单及父菜单可导入的情况下再判断当前职责下菜单是否可导入
                        if ("Y".equals(menu.getImportFlag()) && "Y".equals(parentImportFlag)) {
                            menu.setImportFlag(menu.getDutyImportFlag());
                        } else {
                            menu.setImportFlag("N");
                        }
                    }
                }
            }
        }
        return list;
    }

    /**
     * <p>
     * 合并一个菜单到菜单列表，相同菜单不同权限配置则取大权限作为最终权限
     * </p>
     *
     * @param menu
     * @param list 该列表会被修改
     * @return
     * @author yrf
     */
    @Override
    public List<Menu> menuAuthMerge(Menu menu, List<Menu> list) {
        if (menu == null) {
            return list;
        }
        int size = list.size();
        boolean foundFlag = false;
        for (int i = 0; i < size; i++) {
            Menu menuL = list.get(i);
            if (menu.getId().equals(menuL.getId())) {
                // 菜单id相等
                menuAuthMerge(menu, menuL);
                foundFlag = true;
                break;
            }
        }
        // 在listB中没找到与menuA相等的
        if (!foundFlag) {
            list.add(menu);
        }
        return list;
    }

    /**
     * <p>
     * 合并两个菜单权限，相同菜单不同权限配置则取大权限作为最终权限
     * </p>
     *
     * @param menuA
     * @param menuB 该对象内容会被修改
     * @return
     * @author yrf
     */
    @Override
    public Menu menuAuthMerge(Menu menuA, Menu menuB) {
        // 菜单id相等
        String readOnlyFlag = menuB.getReadonlyFlag();
        String flag = "Y";
        if (flag.equals(readOnlyFlag) && !flag.equals(menuA.getReadonlyFlag())) {
            // menuB只读，menuA不只读，menuA代替menuB
            menuB.setReadonlyFlag(menuA.getReadonlyFlag());
            menuB.setAddFlag(menuA.getAddFlag());
            menuB.setEditFlag(menuA.getEditFlag());
            menuB.setDeleteFlag(menuA.getDeleteFlag());
            menuB.setImportFlag(menuA.getImportFlag());
        } else if (!flag.equals(readOnlyFlag) && !flag.equals(menuA.getReadonlyFlag())) {
            // 两者都不只读，取大的权限
            if (flag.equals(menuA.getAddFlag())) {
                // menuA权限大则取menuA代替当前menuB
                menuB.setAddFlag(menuA.getAddFlag());
            }
            if (flag.equals(menuA.getEditFlag())) {
                menuB.setEditFlag(menuA.getEditFlag());
            }
            if (flag.equals(menuA.getDeleteFlag())) {
                menuB.setDeleteFlag(menuA.getDeleteFlag());
            }
            if (flag.equals(menuA.getImportFlag())) {
                menuB.setImportFlag(menuA.getImportFlag());
            }
        }
        // 可导出权限
        if (flag.equals(menuA.getExportFlag())) {
            menuB.setExportFlag("Y");
        }
        return menuB;
    }

    @Override
    public List<Menu> querySubItemListByDutyItemIdSelect(Menu record) throws Exception {
        return menuMapper.querySubItemListByDutyItemIdSelect(record);
    }

    @Override
    public Menu queryByMenuType(Menu menu) throws ServiceException {
        Menu result = null;
        try {
            result = menuMapper.queryByMenuType(menu);
        } catch (Exception e) {
            LCLogger.withException(e);
            throw new ServiceException(e.getMessage());
        }
        return result;
    }

    @Override
    @DataSource(DataSourceType.RANDOM)
    public List<Menu> queryFstMenuList(CoreUser user) throws ServiceException {
        List<Menu> list = null;
        try {
            list = menuMapper.queryFstMenuList(user);
        } catch (Exception e) {
            LCLogger.withException(e);
            throw new ServiceException(e.getMessage());
        }
        return list;
    }

    @DataSource(DataSourceType.RANDOM)
    @Override
    public List<Menu> queryDmsMenuByUserId(Long userId) throws Exception {
        List<Menu> list = menuMapper.queryDmsMenuByUserId(userId);
        return list;
    }

    @DataSource(DataSourceType.RANDOM)
    @Override
    public List<Menu> queryDmsFstMenuList(Long userId) throws Exception {
        if (null == userId) {
            throw new ServiceException("PUBLIC-022");
        }

        List<Menu> firstLevelList = new ArrayList<Menu>();

        Menu param = new Menu();
        param.setUserId(userId);
        param.setMenuType(null);
        List<Menu> secondLevelList = menuMapper.queryMenuListByTypeSelect(param);


        Menu subParam = new Menu();
        subParam.setUserId(userId);
        param.setMenuType(null);
        // 安全性菜单查询参数
        subParam.setMenuGrade("2");
        List<Menu> thirdLevelList = querySubItemListByDutyItemIdSelect(subParam);
        List<Menu> allMenuList = menuMapper.queryDmsMenuByUserId(userId);

        for (int i = 0; i < allMenuList.size(); i++) {
            Menu row = allMenuList.get(i);
            if (StringUtils.equals(row.getMenuGrade(), "0")) {
                firstLevelList.add(row);
            }
        }

        List<Menu> itemList = null;
        for (int i = 0; i < firstLevelList.size(); i++) {
            Menu firstLevelRow = firstLevelList.get(i);
            itemList = new ArrayList<Menu>();
            for (int j = 0; j < secondLevelList.size(); j++) {
                Menu secondLevelRow = secondLevelList.get(j);
                if (!(secondLevelRow.getParentMenuId().equals(firstLevelRow.getId()))) {
                    continue;
                }
                itemList.add(secondLevelRow);
            }
            itemList = menuAuth(itemList, thirdLevelList, "");
            //移除没有安全性菜单的二级菜单
            Iterator<Menu> it = itemList.iterator();
            while (it.hasNext()) {
                Menu x = it.next();
                if (x.getSubMenus() == null || x.getSubMenus().size() == 0) {
                    it.remove();
                }
            }

            firstLevelRow.setSubMenus(itemList);
        }
        List<Menu> resultList = new ArrayList<Menu>();
        for (int i = 0; i < firstLevelList.size(); i++) {
            Menu item = firstLevelList.get(i);
            if (item.getSubMenus() != null && item.getSubMenus().size() > 0) {
                resultList.add(item);
            }
        }
        return resultList;
    }

    /**
     * <p>
     * 通过用户名查询第一级菜单，利用第一级菜单查询二级菜单和安全性，组织为树形结构并计算安全性的flag
     * </p>
     *
     * @param user    用户名
     * @param session
     * @return
     * @author czy
     */
    @Override
    public Map<String, Object> queryAllMenuByUser(CoreUser user, HttpSession session) throws Exception {
        Map<String, Object> result = new HashMap<String, Object>(4);
        try {
            // 获取用户一级菜单
            List<Menu> list = queryFstMenuList(user);
            if (list != null) {
                Map<String, Object>[] temp = new Map[list.size()];
                //查询当前用户所有二级菜单
                List<Menu> subListAll = getMenuListByMenuTyepSelect("", user.getId(), session);
                Map<Long, List<Menu>> subListAllMap = new HashMap<Long, List<Menu>>(16);
                for (int i = 0; i < subListAll.size(); i++) {
                    //对所有二级菜单根据类型分类，以list存放到map，key为一级菜单id;以前以类型为key，导致一级菜单修改类型后无法正确关联
                    Menu subMenu = subListAll.get(i);
                    List subList = subListAllMap.get(subMenu.getParentMenuId());
                    if (subList == null) {
                        subList = new ArrayList<NewMenu>();
                        subListAllMap.put(subMenu.getParentMenuId(), subList);
                    }
                    List<NewMenu> newMenuList = new ArrayList<NewMenu>();
                    if (null != subMenu.getSubMenus()) {
                        for (Menu menu : subMenu.getSubMenus()) {
                            newMenuList.add(menuToNewMenu(menu));
                        }
                    }
                    NewMenu newMenu = menuToNewMenu(subMenu);
                    newMenu.setSubMenus(newMenuList);
                    subList.add(newMenu);
                }
                // 根据用户一级菜单获取二级菜单和安全性，并使用Map组成树形结构
                for (int i = 0; i < list.size(); i++) {
                    temp[i] = new HashMap<String, Object>(16);
                    temp[i].put("level1", menuToNewMenu(list.get(i)));
                    temp[i].put("level2", subListAllMap.get(list.get(i).getId()));
                }
                result.put("result", temp);
            } else {
                result.put("success", false);
            }
        } catch (Exception e) {
            LCLogger.withException(e);
            result.put("result", e.getMessage());
        }
        return result;
    }

    /**
     * 将Menu对象转换为NewMenu对象
     *
     * @param menu
     * @return
     */
    private NewMenu menuToNewMenu(Menu menu) {
        if (menu == null) {
            return null;
        }
        NewMenu newMenu = new NewMenu();
        newMenu.setId(menu.getId());
        newMenu.setCorpid(menu.getCorpid());
        newMenu.setMenuIcon(menu.getMenuIcon());
        newMenu.setMenuUrl(menu.getMenuUrl());
        newMenu.setText(menu.getText());
        newMenu.setSortNumber(menu.getSortNumber());
        newMenu.setParentMenuId(menu.getParentMenuId());
        newMenu.setParentMenuName(menu.getParentMenuName());
        newMenu.setOauthType(menu.getOauthType());
        newMenu.setOauthSql(menu.getOauthSql());
        newMenu.setMenuType(menu.getMenuType());
        newMenu.setMenuTypeName(menu.getMenuTypeName());
        newMenu.setMenuGrade(menu.getMenuGrade());
        newMenu.setPlatformType(menu.getPlatformType());
        newMenu.setReadonlyFlag(menu.getReadonlyFlag());
        newMenu.setAddFlag(menu.getAddFlag());
        newMenu.setEditFlag(menu.getEditFlag());
        newMenu.setDeleteFlag(menu.getDeleteFlag());
        newMenu.setImportFlag(menu.getImportFlag());
        newMenu.setExportFlag(menu.getExportFlag());
        newMenu.setOpenNewpageFlag(menu.getOpenNewpageFlag());
        newMenu.setDefaultNoQueryFlag(menu.getDefaultNoQueryFlag());
        newMenu.setCommonFlag(menu.getCommonFlag());
        newMenu.setHomePageFlag(menu.getHomePageFlag());
        newMenu.setParentHomePageFlag(menu.getParentHomePageFlag());
        return newMenu;
    }

    /* @Override
    public void beforUpsert(Menu record) throws Exception {
        CoreUser user = UserUtil.getUser();
        if (user == null) {
            throw new ServiceException("PUBLIC-023");
        }
        Long zeros = 0L;
        if (zeros.equals(user.getCorpid())) {
            return;
        } else {
            // 个性化菜单保存更改
            record.setAutoInd("Y");
            record.setUniversalInd("Y");
            record.setFmcgInd("Y");
            record.setHighTechInd("Y");
            record.setFinInd("Y");
            record.setHomeInd("Y");
            record.setRetailInd("Y");
            record.setOauthSql(null);
        }
    } */


    @Override
    public void beforUpsert(Menu menu) throws Exception {
        // 这里需要监控二级菜单（menuGrade =1）,安全性菜单（menuGrade =2）菜单的新增/修改/批量编辑,当变化时需要触发职责url变化
        if (ArrayUtils.contains(MonitorObjectChange.MENU_GRADE_1_2, menu.getMenuGrade()) && "UPDATE".equals(menu.getRow_status())) {
            monitorObjectChange.dutyChange(menu,"CRM_DMS");
        }
    }

    @Override
    public void beforDelete(Menu menu) throws Exception {
        // 这里需要监控二级菜单（menuGrade =1）,安全性菜单（menuGrade =2）菜单的删除,当变化时需要触发职责url变化
        if (ArrayUtils.contains(MonitorObjectChange.MENU_GRADE_1_2, menu.getMenuGrade())) {
            monitorObjectChange.dutyChange(menu,"CRM_DMS");
        }
    }

    @Override
    @Transactional(rollbackFor = ServiceException.class)
    public void fixData() throws Exception {


        //给无安全性菜单的二级菜单添加安全性菜单ALL，并保存映射关系到map
        List<Menu> noSecuritySecondMenuList = menuMapper.selectSecondWithoutSecurity(new Menu());
        Map<Long, Long> menuMapSubMenu = new HashMap<Long, Long>(16);
        for (Menu menu : noSecuritySecondMenuList) {
            Menu subMenu = new Menu();
            Long subId = keyGenerateService.keyGenerate();
            subMenu.setParentMenuId(menu.getId());
            subMenu.setMenuGrade("2");
            subMenu.setOauthType("ALL");
            subMenu.setText("所有的数据");
            subMenu.setSortNumber("1");
            subMenu.setId(subId);
            subMenu.setMenuType(menu.getMenuType());
            subMenu.setPlatformType(menu.getPlatformType());
            subMenu.setAddFlag("Y");
            subMenu.setEditFlag("Y");
            subMenu.setDeleteFlag("Y");
            subMenu.setImportFlag("Y");
            subMenu.setExportFlag("Y");
            subMenu.setReadonlyFlag("N");
            subMenu.setLoginCorpId(menu.getCorpid());
            subMenu.setCorpid(menu.getCorpid());
            menuMapper.insert(subMenu);
            menuMapSubMenu.put(menu.getId(), subId);
        }
        //跟没关联安全性菜单的职责添加安全性菜单关联
        List<Menu> noSecurityDutySecondMenuList = menuMapper.selectDutySecondMapWithoutSecurity(new Menu());
        StringBuilder strb = new StringBuilder();
        for (Menu dutyMenu : noSecurityDutySecondMenuList) {
            Long dutyId = dutyMenu.getDutyId();
            Long menuId = dutyMenu.getUserId();
            if (null == dutyId || null == menuId) {
                strb.append("bad data: ==>(dutyId: " + dutyId + ", menuId: " + menuId + ") \n");
                LCLogger.warn().withMessageKey("fixData")
                        .withMessage("bad data: ==>(dutyId: " + dutyId + ", menuId: " + menuId + ")").flush();
                continue;
            }
            //从map里获取安全性菜单，无则到数据库里查询，默认加上ALL的安全性，再无则不管，人工修复
            Long subMenuId = menuMapSubMenu.get(menuId);
            if (null == subMenuId) {
                Menu example = new Menu();
                example.setPageFlag(false);
                example.setParentMenuId(menuId);
                example.setUnionFlag("N");
                example.setLoginCorpId(dutyMenu.getCorpid());
                example.setCorpid(dutyMenu.getCorpid());
                List<Menu> subList = menuMapper.queryByExamplePage(example);
                for (Menu sub : subList) {
                    if ("ALL".equals(sub.getOauthType()) && StringUtils.isBlank(sub.getOauthSql())) {
                        menuMapSubMenu.put(menuId, sub.getId());
                        subMenuId = sub.getId();
                        break;
                    }
                }
            }
            if (null != subMenuId && null != dutyId) {
                Menu dutyMenuInsert = new Menu();
                dutyMenuInsert.setId(keyGenerateService.keyGenerate());
                dutyMenuInsert.setDutyId(dutyId);
                dutyMenuInsert.setUserId(subMenuId);
                dutyMenuInsert.setCorpid(dutyMenu.getCorpid());
                dutyMenuInsert.setCreatedBy(0L);
                dutyMenuInsert.setLastUpdatedBy(dutyMenu.getCreatedBy());
                menuMapper.addMenuToDuty(dutyMenuInsert);
            } else {
                strb.append("badData==> (dutyId:" + dutyId + ", menuId: " + menuId + ", subMenuId:" + subMenuId + ") \n");
                LCLogger.warn().withMessageKey("fixData")
                        .withMessage("badData==> (dutyId:" + dutyId + ", menuId: " + menuId + ", subMenuId:" + subMenuId + ")").flush();
            }
            System.err.println(strb.toString());
        }
    }

    @Override
    public Long[] getUserMenuIds(Long userId) {

        Long[] results = menuMapper.getUserMenuIds(userId);
        return results;
    }

    /**
     * 把登录用户的id与所有可访问菜单组成的"userId:menuId"字符串加到redis缓存（集合）里，访问校验时使用
     *
     * @param user
     * @author yrf
     */
    @Override
    public void loadUserMenuIdsToRedis(User user) {
        Long[] tempUserMenuIds = getUserMenuIds(user.getId());
        String[] userMenuIds = new String[tempUserMenuIds.length];
        LinkRedisClient jedis = null;
        try {
            jedis = RedisUtil.getJedis();
            String redisKey = AppConstants.USER_MENU_SET + ":" + user.getCorpid() + ":" + user.getId();
            jedis.del(redisKey);
            for (int i = 0; i < tempUserMenuIds.length; i++) {
                userMenuIds[i] = String.valueOf(tempUserMenuIds[i]);
            }
            if (userMenuIds != null && userMenuIds.length > 0) {
                jedis.sadd(redisKey, userMenuIds);
            }
        } finally {
            RedisUtil.returnResource(jedis);
        }
    }

    @Override
    @Transactional(rollbackFor = ServiceException.class)
    public Menu deepCopySecurityMenu(Menu record) throws Exception {

        Menu menu = menuMapper.queryById(record);
        if (menu == null) {
            throw new ServiceException("COMMON-004",record.getId().toString());
        }
        menu.setId(keyGenerateService.keyGenerate());
        menu.setLoginCorpId(null);
        menu.setCorpid(null);
        menu.setText(menu.getText() + "--copy");
        insert(menu);
        SecurityPage securityPageExample = new SecurityPage();
        securityPageExample.setParentId(record.getId());
        List<SecurityPage> securityPages = securityPageService.getPageAndUrlByMenuId(securityPageExample);
        for (SecurityPage page : securityPages) {
            Long pageId = keyGenerateService.keyGenerate();
            page.setParentId(menu.getId());
            page.setId(pageId);
            page.setLoginCorpId(null);
            page.setCorpid(null);
            securityPageService.insert(page);
            List<SecurityPageUrl> urlList = page.getUrlList();
            if (urlList != null && urlList.size() > 0) {
                for (SecurityPageUrl pageUrl : urlList) {
                    pageUrl.setParentId(pageId);
                    pageUrl.setId(keyGenerateService.keyGenerate());
                    pageUrl.setLoginCorpId(null);
                    pageUrl.setCorpid(null);
                    securityPageUrlService.insert(pageUrl);
                }
            }
        }
        return menu;
    }


    /**
     * 分发指定的菜单给指定的账套
     *
     * @param menuAndCorpModel
     * @throws Exception
     */
    @Override
    @Transactional(rollbackFor = ServiceException.class)
    public void distributeMenuByAssign(MenuAndCorpModel menuAndCorpModel) throws Exception {
        //传入的指定账套集合，已逗号分隔
        String corps = menuAndCorpModel.getAssignCorpCode();
        List<String> corpList = Arrays.asList(corps.split(","));

        if (CollectionUtils.isEmpty(corpList)) {
            throw new ServiceException("PUBLIC-024");
        }

        List<Menu> fontMenus = menuAndCorpModel.getMenuModels();
        //分离出一级菜单id   map  返回一个
        List<Long> fontParentId = fontMenus.stream().map(m -> m.getId()).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(fontParentId)) {
            throw new ServiceException("PUBLIC-024");
        }

        //查询出前端指定的一级菜单
        Map<String, Object> fontQp = new TreeMap<>();
        fontQp.put("ids", fontParentId);
        //  是在000账套下查找
        List<Menu> fontQueryMenus = menuMapper.handlerByAssginMenuType(fontQp);

        if (CollectionUtils.isEmpty(fontQueryMenus)) {
            throw new ServiceException("PUBLIC-024");
        }
        //逗号分隔  作为各账套的查询条件
        List<String> menuTypes = fontQueryMenus.stream().map(f -> f.getMenuType()).collect(Collectors.toList());
        //整合一二级菜单
        fontQueryMenus.stream().forEach(m -> {
            fontMenus.stream().forEach(f -> {
                if (m.getId().equals(f.getId())) {
                    if (!CollectionUtils.isEmpty(f.getChildren())) {
                        m.setChildren(f.getChildren());
                    }
                }
            });
        });
        /**
         * 遍历每一个账套
         */
        corpList.stream().forEach(cc -> {
            try {
                forEachCorp(fontQueryMenus, menuTypes, Long.parseLong(cc));
            } catch (Exception e) {
                LCLogger.withException(e);
                throw new ServiceException(e.getMessage());
            }
        });
    }


    /***
     * 遍历每一个账套
     * @param fontQueryMenus
     * @param menuTypes
     * @param corpID
     * @throws Exception
     */
    public void forEachCorp(List<Menu> fontQueryMenus, List<String> menuTypes, Long corpID) throws Exception {
        //此处需要一个for 循环， 遍历 每一个账套，现在仅写了一个
        //查询指定账套的菜单   返回的数据都已经存在的，此时找出不存在的
        Map<String, Object> assignQp = new TreeMap<>();
        //指派账套
        assignQp.put("corpId", corpID);
        assignQp.put("MenuTypes", menuTypes);
        //平台
        assignQp.put("platformType", "CRM");
        //等级  一级
        assignQp.put("menuGrade", "0");
        //  是在102账套下查找
        List<Menu> assignMenus = menuMapper.handlerByAssginMenuType(assignQp);
        //创建全部
        if (CollectionUtils.isEmpty(assignMenus)) {
            batchNewMenus(fontQueryMenus, corpID, "0");
            //创建所有
        } else {  //需要筛选数据

            //获取一级菜单编号
            List<String> assiginMenuTypes = assiginMenuTypes = assignMenus.stream().map(s -> s.getMenuType()).collect(Collectors.toList());


            //查询该账套下的菜单不存在
            if (CollectionUtils.isEmpty(assiginMenuTypes)) {
                //创建所有 fontQueryMenus
                batchNewMenus(fontQueryMenus, corpID, "0");
            } else {
                //lambda中使用外部变量，默认是final
                List<String> finalAssiginMenuTypes = assiginMenuTypes;
                //筛选不存在的数据
                //前端不包含在查询的指定账套下的菜单编号  完全新建
                List<Menu> newMenus = fontQueryMenus.stream().filter(f -> {

                    return !finalAssiginMenuTypes.contains(f.getMenuType());
                }).collect(Collectors.toList());

                // 筛选存在的数据
                //前端包含在查询的指定账套下的菜单编号  部分更新
                List<Menu> updateMenus = fontQueryMenus.stream().filter(f -> {
                    return finalAssiginMenuTypes.contains(f.getMenuType());
                }).collect(Collectors.toList());

                //updateMenus 存在一级菜单的，检测二级菜单是否存在，如果不存在那么就新建
                assignMenus.stream().forEach(a -> {
                    updateMenus.stream().forEach(u -> {
                        if (StringUtils.equals(a.getMenuType(), u.getMenuType())) {
                            //根据当前的  a 的id为头+ u的menuUrl 条件  查找是否存在，不存在的插入
                            /***
                             *   1  查看 u中二级菜单是否存在存在向下执行  ，不存在，不处理
                             *   查找  a.id+u.menuUrl  返回二级菜单结果
                             *   if 不存在 进行插入
                             *   存在不操作
                             */
                            try {

                                if (!CollectionUtils.isEmpty(u.getChildren())) {
                                    u.getChildren().stream().forEach(c -> {
                                        //查找指定的二级菜单
                                        Map<String, Object> lmqp = new TreeMap<>();
                                        //父菜单id
                                        lmqp.put("parentMenuId", a.getId());
                                        //查找的url
                                        lmqp.put("menuUrl", c.getMenuUrl());
                                        //平台
                                        lmqp.put("platformType", "CRM");
                                        //等级  一级
                                        lmqp.put("menuGrade", "1");
                                        List<Menu> childMenu = null;
                                        try {
                                            //  是在102账套下查找
                                            childMenu = menuMapper.handlerByAssginMenuType(lmqp);
                                        } catch (Exception e) {
                                            LCLogger.withException(e);
                                            throw new ServiceException(e.getMessage());
                                        }
                                        //如果没有查找到 那么就新建，，查找到不处理
                                        if (CollectionUtils.isEmpty(childMenu)) {
                                            Long cid = keyGenerateService.keyGenerate();
                                            Menu resetMenu = c;
                                            //自身id
                                            resetMenu.setId(cid);
                                            //父id
                                            resetMenu.setParentMenuId(a.getId());
                                            //corpid
                                            resetMenu.setCorpid(a.getCorpid());
                                            //等级
                                            resetMenu.setMenuGrade("1");
                                            try {
                                                newMenu(resetMenu, "1");
                                            } catch (Exception e) {
                                                LCLogger.withException(e);
                                                throw new ServiceException(e.getMessage());
                                            }
                                        }
                                    });
                                }
                            } catch (Exception e) {
                                LCLogger.withException(e);
                                throw new ServiceException(e.getMessage());
                            }
                        }
                    });
                });

                //不存在的 全部新建
                if (!CollectionUtils.isEmpty(newMenus)) {
                    batchNewMenus(newMenus, corpID, "0");
                }
            }
        }


        //查找到  指定账套
        /**
         * 1 获取传入的一级菜单id，并查询出结果 distributionOneMenus
         *  1.1 从查询的 distributionOneMenus 分离出所有的 菜单编码  menuTypes
         *
         *
         * 2 获取指定的账套集合  assignCorpIds
         *
         * 3 获取指定账套下的菜单，此时我不想获取所有，仅仅通过 菜单编码去查询指定的一级菜单
         *  3.1  查询条件  (in)menuTypes + corpId   得到一级  assignCorpMenus
         *
         * 4
         *
         *
         */
    }

    /***
     * 批量创建
     * 创建一级和二级
     * @param menus  接收一级集合，一级包含二级集合
     */
    public void batchNewMenus(List<Menu> menus, Long corpid, String grade) throws Exception {
        menus.stream().forEach(m -> {
            try {
                Long hid = keyGenerateService.keyGenerate();
                m.setId(hid);
                m.setCorpid(corpid);
                m.setMenuGrade(grade);
                newMenu(m, grade);
                if (!CollectionUtils.isEmpty(m.getChildren())) {
                    m.getChildren().stream().forEach(c -> {
                        try {
                            Long cid = keyGenerateService.keyGenerate();
                            c.setCorpid(corpid);
                            c.setId(cid);
                            c.setParentMenuId(hid);
                            c.setMenuGrade("1");
                            newMenu(c, "1");
                        } catch (Exception e) {
                            LCLogger.withException(e);
                            throw new ServiceException(e.getMessage());
                        }
                    });
                }
            } catch (Exception e) {
                LCLogger.withException(e);
                throw new ServiceException(e.getMessage());
            }
        });
    }

    /**
     * 新建
     *
     * @param menu  菜单
     * @param grade 指定等级  0、1
     *              创建指定等级
     */
    public void newMenu(Menu menu, String grade) throws Exception {
        Menu nMenu = new Menu();
        //id 必须重置
        nMenu.setId(menu.getId());
        //corpID  需要重置成 指定的账套
        nMenu.setCorpid(menu.getCorpid());
        nMenu.setText(menu.getText());
        nMenu.setSortNumber(menu.getSortNumber());
        nMenu.setMenuType(menu.getMenuType());
        //等级
        nMenu.setMenuGrade(menu.getMenuGrade());
        nMenu.setMenuUrl(menu.getMenuUrl());
        nMenu.setPlatformType(menu.getPlatformType());
        String zero = "0";
        String one = "1";
        //一级菜单
        if (StringUtils.equals(grade, zero)) {
            nMenu.setHomePageFlag(menu.getHomePageFlag());
            nMenu.setMenuIcon(menu.getMenuIcon());
            //二级菜单
        } else if (StringUtils.equals(grade, one)) {
            //需要设置父id  parentMenuId
            nMenu.setParentMenuId(menu.getParentMenuId());

            nMenu.setComments(menu.getComments());
            nMenu.setReadonlyFlag(menu.getReadonlyFlag());
            nMenu.setAddFlag(menu.getAddFlag());
            nMenu.setEditFlag(menu.getEditFlag());
            nMenu.setDeleteFlag(menu.getDeleteFlag());
            nMenu.setExportFlag(menu.getExportFlag());
            nMenu.setImportFlag(menu.getImportFlag());
            nMenu.setUniversalInd(menu.getUniversalInd());
            nMenu.setFmcgInd(menu.getFmcgInd());
            nMenu.setHighTechInd(menu.getHighTechInd());
            nMenu.setHomeInd(menu.getHomeInd());
            nMenu.setFinInd(menu.getFinInd());
            nMenu.setRetailInd(menu.getRetailInd());
            nMenu.setAutoInd(menu.getAutoInd());
            nMenu.setDefaultNoQueryFlag(menu.getDefaultNoQueryFlag());
            nMenu.setOpenNewpageFlag(menu.getOpenNewpageFlag());
            nMenu.setCommonFlag(menu.getCommonFlag());
        }
        /***
         * 一二级 通用
         text: Test01
         sortNumber: 1.00
         menuType: Test01
         menuGrade: 0
         menuUrl: /
         platformType: CRM

         -- 一级私有
         homePageFlag: N
         menuIcon: 1
         */

        menuMapper.insert(nMenu);
    }

    /**
     * 查询当前职责内的所有DMS一级菜单以及二级菜单
     */
    @Override
    public List<Menu> queryAllDmsMenuAndSubMenu(Menu entity) throws Exception {
        List<Menu> menuList = menuMapper.queryByExamplePage(entity);
        if (menuList.size() > 0) {
            menuList.forEach(menu -> {
                try {
                    Menu menuObj = new Menu();
                    menuObj.setParentMenuId(menu.getId());
                    menuObj.setMenuGrade("1");
                    menuObj.setPageFlag(false);
                    menuObj.setIndepDistri("Y");
                    menuObj.setPlatformType("DMS");
                    menuObj.setUnionFlag("Y");
                    List<Menu> subMenuList = menuMapper.queryByExamplePage(menuObj);
                    menu.setSubMenus(subMenuList);
                } catch (Exception e) {
                    LCLogger.withException(e);
                    throw new ServiceException(e.getMessage());
                }
            });
        }
        return menuList;
    }

    @Override
    public List<MenuTree> querySafetyMenuList(Menu entity) throws Exception {
        List<MenuTree> menuTreeList = menuMapper.querySafetyMenuList(entity);
        if (menuTreeList != null && menuTreeList.size() > 0) {
            Map<String, Integer> result = new HashMap<>(4);
            for (MenuTree record : menuTreeList) {
                String recordKey = record.getParentMenuId().toString();
                if (result.containsKey(recordKey)) {
                    Integer num = result.get(recordKey);
                    num++;
                    result.replace(recordKey, num);
                } else {
                    result.put(recordKey, 1);
                    record.setIsShow("Y");
                }
            }
            for (MenuTree record : menuTreeList) {
                if ("Y".equals(record.getIsShow())) {
                    Integer totalNum = result.get(String.valueOf(record.getParentMenuId()));
                    record.setShowNum(totalNum.toString());
                }
            }
        }
        return menuTreeList;
    }

}
